package com.qiangzi.workflow.engine.service.impl;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.qiangzi.workflow.engine.bpmn.base.Action;
import com.qiangzi.workflow.engine.bpmn.base.Edge;
import com.qiangzi.workflow.engine.bpmn.base.Node;
import com.qiangzi.workflow.engine.bpmn.base.ProcessInstance;
import com.qiangzi.workflow.engine.bpmn.base.State;
import com.qiangzi.workflow.engine.core.MyEngine;
import com.qiangzi.workflow.engine.core.NodeExecution;
import com.qiangzi.workflow.engine.service.RuntimeService;

public class MemoryRuntimeService implements RuntimeService {

	public static final Logger LOGGER = LoggerFactory.getLogger(MemoryRuntimeService.class);

	@Override
	public void run(ProcessInstance instance, MyEngine engine) throws Exception {
		Assert.isTrue(null != instance && instance.getDeployThread() == Thread.currentThread(),
				"deploy thread must be the same as run thread");
		run0(instance, engine);

	}

	/**
	 * 这里返回的节点可能是-[确实需要执行|需要被忽略]
	 * 
	 * @param candidates
	 * @return
	 * @throws Exception
	 */
	private NodeExecution getNextExecution(Map<String, Node> candidates) throws Exception {

		Collection<Node> nodes = candidates.values();

		for (Node node : nodes) {

			// 当前节点必须是NEW状态
			if (State.NEW != node.getState()) {
				throw new Exception("wrong node state " + node.getId());
			}

			// 当前的点的状态是NEW-表示没有执行过,下面判断是否满足条件
			List<Edge> edges = node.getIncoming();

			// 1)如果没有边,当然可以执行
			if (null == edges || 0 == edges.size()) {
				NodeExecution execution = new NodeExecution();
				execution.setNode(node);
				execution.setAction(Action.INVOKE);
				return execution;
			}

			// 2)聚合边的状态
			int state = 0;
			for (Edge edge : edges) {
				state |= edge.getState();
			}

			// 如果存在NEW的边,肯定不会被执行
			int newState = State.NEW & state;
			if (0 != newState) {
				// 继续判断下一个节点
				continue;
			}

			// 既然所有的边都已经执行过了,那么就可以判断当前节点要执行的动作
			int invokedState = State.INVOKED & state;
			NodeExecution execution = new NodeExecution();
			execution.setNode(node);
			execution.setAction(0 != invokedState ? Action.INVOKE : Action.IGNORE);
			return execution;

		}

		return null;

	}

	private void run0(ProcessInstance instance, MyEngine engine) throws Exception {

		// 需要强调的一点是:点执行完以后顺便触发相关的边的执行,这是因为边的执行实际上会依赖于点的类型
		// 所以这个点的出边的状态交给这个点的invoke动作来触发
		NodeExecution nodeExecution;
		while (null != (nodeExecution = getNextExecution(instance.getCandidates()))) {

			// 取出
			instance.removeCandidate(nodeExecution.getNode());

			// 执行
			Node node = nodeExecution.getNode();
			Action action = nodeExecution.getAction();

			if (Action.INVOKE == action) {
				node.invoke(instance, engine);
			} else {
				node.ignore(instance);
			}

			// 补充新节点
			List<Edge> edges = node.getOutgoing();
			for (Edge edge : edges) {
				Node targetNode = edge.getTargetNode();
				// 重复的会覆盖,因为内部用了hashmap,没有关系
				instance.addCandidate(targetNode);
			}

		}

		Assert.isTrue(instance.getCandidates().isEmpty(), "not all node executed ,check your xml config");
		LOGGER.info("instance execute done, succeed");

	}

}
